OOP Concepts — GDScript
Skill codes on this page
| Code | Level | Skill |
|---|---|---|
| C643 | 7–8 | access modifiers |
| C651 | 9–10 | classes |
| C652 | 9–10 | objects |
| C653 | 9–10 | abstraction |
| C654 | 9–10 | encapsulation |
| C655 | 9–10 | generalisation |
| C656 | 9–10 | inheritance |
Definition. Object-oriented programming (OOP): programming based on objects that contain data in the form of fields or attributes, and code in the form of methods.
Level 9–10 asks for classes and objects plus all relevant OOP principles — one class on its own does not reach the top band.
C651 — Classes and C652 — Objects
Definitions. Class: a program code template for creating objects. Object: any instantiated class that a program can inspect and/or change. Instantiation: the process by which an object is created from a class.
# MykiCard.gd class_name MykiCard # C651 — class: the template for every card extends RefCounted var _balance: float = 20.00 func top_up(amount: float) -> void: _balance += amount
# C652 — objects: two cards from one template, separate balances var my_card: MykiCard = MykiCard.new() var your_card: MykiCard = MykiCard.new() my_card.top_up(10.0)
Earns the tick: C651 — a class you wrote (class_name, fields, methods). C652 — .new() creating an object your program then uses. Two objects holding different state is the cleanest demo.
In tile-matching-game: card.gd opens with class_name Card — fields (pair_id, is_face_up) plus methods (flip_up(), mark_matched()) in one template (C651). The game then holds two of them:
# tile-matching-game — game.gd var first_card: Card = null # C652 — two objects from one class, var second_card: Card = null # each with its own state
(The board creates them with card_scene.instantiate() — instancing the card scene constructs a Card object, because the scene's root node runs card.gd.)
In club-ladder — the direct .new() case:
# club-ladder — app.gd season.teams.append(Team.new(team_name)) # C652 — object created and used
season.gd builds them four arguments at a time from the XML — Team.new(team_name, wins, draws, losses) — and Team._init gives every parameter a default, so both calls work.
C653 — Abstraction
Definition. An OOP principle that allows programmers to manage complexity by hiding implementation details and exposing only the essential features of an object.
How it works in GDScript:
- Base class defines structure meant for inheritance
- Methods can have default implementations or error messages
- Convention-based — not meant to be used directly
assert(false)is one way to indicate override needed- Focus on inheritance structure rather than enforcement
# C653 — abstraction: base class with common structure class_name Animal extends RefCounted var name: String func _init(p_name: String): name = p_name func make_sound(): print("Some animal sound") func move(): print(name + " is moving")
Myki version:
# C653 — abstraction: callers use get_cost() and never see the fare rules class_name Journey extends RefCounted var fare: float func get_cost() -> float: return fare
Earns the tick: a base class used through its simple interface while the details stay inside. "The HUD calls get_cost() and doesn't care how the cost is worked out" is the sentence to say.
In tile-matching-game: card.gd's own header comment makes the abstraction argument — a card "does NOT decide whether two cards match — that is the Game's job. The card only reports being clicked." The game calls flip_up() and mark_matched(); how a card draws itself stays hidden inside the card.
C654 — Encapsulation
Definition. An OOP principle that involves bundling the data, and the methods that operate on that data, into a single unit or class.
How it works in GDScript:
- Single underscore
_indicates private by convention only - No enforcement mechanism — purely documentation
- All class members are technically public
- Relies entirely on developer discipline
- IDE may highlight convention violations
class_name BankAccount extends RefCounted var _balance: float = 0.0 # C654 — private by convention func get_balance() -> float: return _balance func deposit(amount: float): if amount > 0: _balance += amount print("Deposited $" + str(amount)) else: print("Invalid amount")
Myki version:
class_name MykiCard extends RefCounted var _balance: float = 20.00 # C654 — private, cannot be changed directly func _is_valid(amount: float) -> bool: # C654 — private helper, used only inside this class return amount > 0 func top_up(amount: float) -> void: # C654 — the only door to _balance if _is_valid(amount): _balance += amount
Earns the tick: data behind _, changed only through methods that validate. In validation: show top_up(-50) being rejected.
In tile-matching-game: Card bundles its state (pair_id, is_face_up, is_matched) with the methods that change it — outside code never sets those fields, it calls flip_up(), flip_down(), mark_matched(). (The game reads them directly; underscore-prefixing the fields and adding getters would push this to full-marks encapsulation.)
C643 — Access modifiers
GDScript has no private / public keywords — the _ prefix is the access modifier, applied by convention:
var _balance: float = 20.00 # C643 — "private": underscore convention func get_balance() -> float: # C643 — public getter is the only way in return _balance
Earns the tick: the convention applied consistently and named — "the underscore marks it private by convention; other code must go through the getter."
In connect-go-dots: _open() and _on_back_pressed() carry the underscore as internal helpers no other script should call. (_ready() also starts with _, but that is Godot's callback naming, not privacy.)
In tile-matching-game: Card shows the split in one class — public flip_up() / mark_matched() for other scripts, private _on_pressed() for itself.
C655 — Generalisation
Definition. The process of defining a general class (superclass) that encapsulates common attributes and behaviours of more specific classes (subclasses).
How it works in GDScript:
class_namemakes class globally accessibleextends RefCountedfor automatic memory management- Constructor
_initsets up shared data - Base class defines common interface for all subclasses
# C655 — generalisation: common functionality in base class class_name Vehicle extends RefCounted var brand: String var model: String var is_running: bool = false func _init(p_brand: String, p_model: String): brand = p_brand model = p_model func start_engine(): is_running = true print(brand + " " + model + " started") func stop_engine(): is_running = false print(brand + " stopped")
Myki version:
# C655 — generalisation: what every journey shares lives in the parent class_name Journey extends RefCounted var zone: int var fare: float func _init(p_zone: int, p_fare: float): zone = p_zone fare = p_fare func get_cost() -> float: return fare
Earns the tick: shared fields and methods pulled up into one parent instead of repeated in each child — point at the duplication you avoided.
C656 — Inheritance
Definition. A method of basing an object or class on another object or class, taking on its attributes and methods and potentially extending upon them.
How it works in GDScript:
extendsestablishes parent-child relationshipsuper()calls parent constructor with parameters- Override by redefining method with same name
- Single inheritance model (one parent)
- Parent methods available unless explicitly overridden
# C656 — inheritance: Car is a Vehicle with extras class_name Car extends Vehicle var doors: int func _init(p_brand: String, p_model: String, p_doors: int): super(p_brand, p_model) doors = p_doors func honk(): print("Beep beep!") # C656 — override parent method func start_engine(): super.start_engine() print("Car is ready to drive")
Myki version:
# C656 — inheritance: a ConcessionJourney is a Journey with one changed rule class_name ConcessionJourney extends Journey func get_cost() -> float: # override return fare * 0.5
Earns the tick: extends your own class, super used, at least one method overridden — and both parent and child actually used by the running program.
Not enough: every script in connect-go-dots has extends Control or extends Node — inheriting from an engine class. Necessary in Godot, but C656 wants extends on a class of your own.
Worth seeing anyway: in tile-matching-game, Card extends Button shows what inheriting buys — the card never defines pressed or disabled, yet uses both, because they come from Button. That is inheritance working; it just is not your parent class, so on its own it does not earn C656.
Check Your Understanding
- Why does
extends Controlnot earn C656?
Answer
It inherits from an engine class — the tick needs extends on a class you wrote.
- What is GDScript's access modifier, and which code is it?
Answer
The _ underscore prefix, applied by convention — C643.
- List the minimum ingredients of full C656 evidence.
Answer
Your own parent class, extends on the child, super used, at least one override — and both classes used by the running program.
